//
//  PTKeyCombo.m
//  Protein
//
//  Created by Quentin Carnicelli on Sat Aug 02 2003.
//  Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved.
//

#import "PTKeyCombo.h"
#import "PTKeyCodeTranslator.h"

@implementation PTKeyCombo

+ (id)clearKeyCombo
{
    return [self keyComboWithKeyCode: -1 modifiers: -1];
}

+ (id)keyComboWithKeyCode: (NSInteger)keyCode modifiers: (NSUInteger)modifiers
{
    return [[[self alloc] initWithKeyCode: keyCode modifiers: modifiers] autorelease];
}

- (id)initWithKeyCode: (NSInteger)keyCode modifiers: (NSUInteger)modifiers
{
    self = [super init];

    if( self )
    {
        mKeyCode = keyCode;
        mModifiers = modifiers;
    }

    return self;
}

- (id)initWithPlistRepresentation: (id)plist
{
    int keyCode, modifiers;

    if( !plist || ![plist count] )
    {
        keyCode = -1;
        modifiers = -1;
    }
    else
    {
        keyCode = [[plist objectForKey: @"keyCode"] intValue];
        if( keyCode < 0 ) keyCode = -1;

        modifiers = [[plist objectForKey: @"modifiers"] intValue];
        if( modifiers <= 0 ) modifiers = -1;
    }

    return [self initWithKeyCode: keyCode modifiers: modifiers];
}

- (id)plistRepresentation
{
    return [NSDictionary dictionaryWithObjectsAndKeys:
                [NSNumber numberWithInteger: [self keyCode]], @"keyCode",
                [NSNumber numberWithInteger: [self modifiers]], @"modifiers",
                nil];
}

- (id)copyWithZone:(NSZone*)zone;
{
    return [self retain];
}

- (BOOL)isEqual: (PTKeyCombo*)combo
{
    return    [self keyCode] == [combo keyCode] &&
            [self modifiers] == [combo modifiers];
}

#pragma mark -

- (NSInteger)keyCode
{
    return mKeyCode;
}

- (NSUInteger)modifiers
{
    return mModifiers;
}

- (BOOL)isValidHotKeyCombo
{
    return mKeyCode >= 0 && mModifiers > 0;
}

- (BOOL)isClearCombo
{
    return mKeyCode == -1 && mModifiers == -1;
}

@end

#pragma mark -

@implementation PTKeyCombo (UserDisplayAdditions)

+ (NSString*)_stringForModifiers: (long)modifiers
{
    static unichar modToChar[4][2] =
    {
        { cmdKey,         kCommandUnicode },
        { optionKey,    kOptionUnicode },
        { controlKey,    kControlUnicode },
        { shiftKey,        kShiftUnicode }
    };

    NSString* str = [NSString string];
    long i;

    for( i = 0; i < 4; i++ )
    {
        if( modifiers & modToChar[i][0] )
            str = [str stringByAppendingString: [NSString stringWithCharacters: &modToChar[i][1] length: 1]];
    }

    return str;
}

+ (NSDictionary*)_keyCodesDictionary
{
    static NSDictionary* keyCodes = nil;

    if( keyCodes == nil )
    {
        NSURL *url = [NSURL fileURLWithPath:[[NSBundle bundleForClass: self] pathForResource: @"PTKeyCodes" ofType: @"plist"]];
        NSString *contents = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:NULL];
        keyCodes = [[contents propertyList] retain];
    }

    return keyCodes;
}

+ (NSString*)_stringForKeyCode: (short)keyCode legacyKeyCodeMap: (NSDictionary*)dict
{
    id key;
    NSString* str;

    key = [NSString stringWithFormat: @"%d", keyCode];
    str = [dict objectForKey: key];

    if( !str )
        str = [NSString stringWithFormat: @"%X", keyCode];

    return str;
}

+ (NSString*)_stringForKeyCode: (short)keyCode newKeyCodeMap: (NSDictionary*)dict
{
    NSString* result;
    NSString* keyCodeStr;
    NSDictionary* unmappedKeys;
    NSArray* padKeys;

    keyCodeStr = [NSString stringWithFormat: @"%d", keyCode];

    //Handled if its not handled by translator
    unmappedKeys = [dict objectForKey:@"unmappedKeys"];
    result = [unmappedKeys objectForKey: keyCodeStr];
    if( result )
        return result;

    //Translate it
    result = [[[PTKeyCodeTranslator currentTranslator] translateKeyCode:keyCode] uppercaseString];

    //Handle if its a key-pad key
    padKeys = [dict objectForKey:@"padKeys"];
    if( [padKeys indexOfObject: keyCodeStr] != NSNotFound )
    {
        result = [NSString stringWithFormat:@"%@ %@", [dict objectForKey:@"padKeyString"], result];
    }

    return result;
}

+ (NSString*)_stringForKeyCode: (short)keyCode
{
    NSDictionary* dict;

    dict = [self _keyCodesDictionary];
    if( [[dict objectForKey: @"version"] intValue] <= 0 )
        return [self _stringForKeyCode: keyCode legacyKeyCodeMap: dict];

    return [self _stringForKeyCode: keyCode newKeyCodeMap: dict];
}

- (NSString*)keyCodeString
{
    // special case: the modifiers for the "clear" key are 0x0
    if ( [self isClearCombo] ) return @"";

    return [[self class] _stringForKeyCode:[self keyCode]];
}

- (NSUInteger)modifierMask
{
    // special case: the modifiers for the "clear" key are 0x0
    if ( [self isClearCombo] ) return 0;

    static NSUInteger modToChar[4][2] =
    {
        { cmdKey,         NSCommandKeyMask },
        { optionKey,    NSAlternateKeyMask },
        { controlKey,    NSControlKeyMask },
        { shiftKey,        NSShiftKeyMask }
    };

    NSUInteger i, ret = 0;

    for ( i = 0; i < 4; i++ )
    {
        if ( [self modifiers] & modToChar[i][0] ) {
            ret |= modToChar[i][1];
        }
    }

    return ret;
}

- (NSString*)description
{
    NSString* desc;

    if( [self isValidHotKeyCombo] ) //This might have to change
    {
        desc = [NSString stringWithFormat: @"%@%@",
                [[self class] _stringForModifiers: [self modifiers]],
                [[self class] _stringForKeyCode: [self keyCode]]];
    }
    else
    {
        desc = NSLocalizedString( @"(None)", @"Hot Keys: Key Combo text for 'empty' combo" );
    }

    return desc;
}

@end
